package ui.graphs
{		
	/**
	 * This class handles the creation and animating of
	 * the 3 different graphs. 
	 */	
	import com.greensock.TweenLite;
	import com.greensock.easing.*;
	import com.greensock.plugins.TintPlugin;
	import com.greensock.plugins.TweenPlugin;
	
	import events.Static_EventDispatcher;
	
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	import flash.net.LocalConnection;
	import flash.ui.*;
	import flash.utils.Timer;
	
	TweenPlugin.activate([TintPlugin]);
	
	public class Graph extends Sprite
	{
		// Event constants
		public static const CLEAN_UP			: String = "Clean.Up";
		public static const AUDIO_CHANGE		: String = "Audio.Change";
		public static const MOTION_CHANGE_LOW	: String = "Motion.Change.Low";
		public static const MOTION_CHANGE_MOD	: String = "Motion.Change.Mod";
		public static const PEOPLE_ADD_MINUS	: String = "People.Add.Minus";
		
		// Easing constants
		public static const LINEAR_EASE 		: String = "Linear.easeNone";
		public static const SINE_EASE 			: String = "Sine.easeInOut";
		public static const QUAD_EASE 			: String = "Quad.easeInOut";
		
		// Graph Types
		public static const PEOPLE_GRAPH		: String = "People.Graph";
		public static const MOTION_GRAPH		: String = "Motion.Graph";
		public static const AUDIO_GRAPH			: String = "Audio.Graph";
		
		private var _graphType					: String = "";
		
		private var _graphObj					: Object = new Object();
		private var _motion						: Motion;
		private var _people						: People;
		private var _audio						: Audio;
		
		private const TIME 						: int = 1;
		
		// Settings
		private var _length						: uint = 0;
		private var _easing 					: String = "";
		private var _secEasing 					: String = "";
		private var _tweenTime1 				: int = 1;
		private var _tweenTime2 				: int = 1;
		private var _xPos 						: int = 0;
		private var _yPos						: int = 0;
		private var _dummyData					: Array;
		private var _ground						: int = 0;
		
		// ---
		// Misc variables
		private var _canvas 					: Sprite;
		private var _obj 						: Object;

		private var _point						: int = 0;
			
		private var _motionPoint 				: int = 0;
		private var _peoplePoint				: int = 0;
		private var _audioPoint					: int = 0;
		
		private var _canvasWidth				: Number = 0;
		
		private var _graphIteration				: uint = 1;
		private var _graphArr					: Array = new Array();
		
		private var _p							: Point;
		
		private var _peopleTimer				: Timer;
		private var _peepGrapBool				: Boolean = false;		
		
		private var _counter : Number = 1;
		
		// Test data
		private var _testArr : Array = new Array();
		
		/**
		 * Use this class to creating a fake 3D line graph.
		 *  
		 * @param $easing
		 * @param $secEasing
		 * @param $tweenTime1
		 * @param $tweenTime2
		 * @param $xPos
		 * @param $yPos
		 */		
		public function Graph($length:uint, $graphType:String, $easing:String, $secEasing:String, 
							  $tweenTime1:Number, $tweenTime2:Number, $xPos:int, $yPos:int, $dummyData:Array)
		{
			this._length = $length;
			this._graphType = $graphType;
			this._easing = $easing;
			this._secEasing = $secEasing;
			this._tweenTime1 = $tweenTime1;
			this._tweenTime2 = $tweenTime2;
			this._xPos = $xPos;
			this._yPos = $yPos;
			this._dummyData = $dummyData;
			
			_obj = {};
			
			_obj.x = _xPos;
			_obj.y = _yPos;
			_ground = _obj.y;
			
			// Setting test data
			for (var i : Number = 0; i < _dummyData.length; i++)
				_testArr.push(_convertNum(_dummyData[i]));
			
			TweenLite.delayedCall(1, _createCanvas);
		}
		
		/**
		 * Creates the cavas and adds it to the stage. Also sets the height and width
		 * of the canvas for the point to be displayed in. Method also creates an 
		 * eventListener that throws the _loop on ENTER_FRAME. 
		 * *NOTE: The _randomTween(o:Object) method is called then called again so that
		 * _getRandomData() and _randomTween are called continously.
		 * 
		 * @param width
		 * @param height
		 */		
		private function _createCanvas(width:int = 500, height:int = 400) : void
		{
			_canvas = new Sprite();
			this.cacheAsBitmap = true;
			_canvas.cacheAsBitmap = true;
			_canvas.graphics.lineStyle(1.5, 0x666666, 0, false);
			_canvas.visible = false;
			
			addChild(_canvas);
			
			
			if (_graphType == "People.Graph")
			{
				_peopleTimer = new Timer(500, 0);
				_peopleTimer.addEventListener(TimerEvent.TIMER, _startStopLoop, false, 0, true);
				_peopleTimer.start();
				TweenLite.delayedCall(1.5, _moveLine);
			}
			else
			{
				_canvas.addEventListener(Event.ENTER_FRAME, _loop, false, 0, true);
				TweenLite.delayedCall(1.5, _moveLine);
			}
		}
		
		private function _startStopLoop($e:TimerEvent) : void
		{
			if (!_peepGrapBool)
			{
				_peepGrapBool = true;
				_canvas.addEventListener(Event.ENTER_FRAME, _loop, false, 0, true);
				_obj.x = _xPos;
				_obj.y = _yPos;
				
				_moveLine();
			}
			else
			{
				_peepGrapBool = false;
				_canvas.removeEventListener(Event.ENTER_FRAME, _loop, false);
				
				_xPos = _obj.x;
				_yPos = _obj.y;
				
				TweenLite.killTweensOf(_obj);
			}
		}
		
		/** 
		 * Method that loops through the positioning of
		 * where the point is at any given moment per frame.
		 */
		private function _loop(e:Event) : void
		{
			if (_graphType != "Audio.Graph")
				_canvas.x -= 5;	// Moving canvas 4-pixels to the left
			else
				_canvas.x -= 5;
			
			_p = _canvas.globalToLocal(new Point(_obj.x, _obj.y));
			_canvas.graphics.lineTo(_p.x, _p.y);
			_canvasWidth = _canvas.width;
	
			// Using generic programming to set the movieClip as
			// an object then casting back to MoveClip so I don't
			// have to type out a bunch of redundant code.
			_motion = new Motion();
			_people = new People();
			_audio = new Audio();
			switch (_graphType)
			{
				case "Motion.Graph": _graphObj = _motion; break;
				case "People.Graph": _graphObj = _people; break;
				case "Audio.Graph": _graphObj = _audio;
			}
			
			MovieClip(_graphObj).y = _canvas.y + _p.y;
			addChild(MovieClip(_graphObj));
			
			_graphArr.push(MovieClip(_graphObj));
			
			for (var i : int = 0; i < _graphArr.length; i++)
			{
				_graphArr[i].x -= 3;
			}
					
			if (_graphArr.length >= _length)
			{
				for (var o : Number = 0; o < 30; o++)
				{	
					TweenLite.to(_graphArr[o], 2, {alpha:.3, tint:0x000000});
				}
				
				removeChild(_graphArr[0]);
				_graphArr[0] = null;
				_graphArr.shift();
			}
			
			//*trace ("Mem in use: " + System.totalMemory);
			
								   //36544512
			//if (System.totalMemory > 45000000)
			//{
			//	Static_EventDispatcher.dispatchEvent(new Event(Graph.CLEAN_UP));
			//}
		}
		
		// Replacing _randomTween(o:Object)
		// Sine.easeInOut/Quad.easeInOut/Linear.easeNone
		private function _moveLine() : void
		{
			// This is a bit convoluted so remember to shrink this down
			// a bit when you have the time.
			if (_graphType != "Audio.Graph")
				TweenLite.delayedCall(1, _runRandomMinMax);
			else
				TweenLite.delayedCall(.7, _destroyTween);	
			
				if (_graphType != "Audio.Graph")
				{
					switch (_graphType)
					{
						case "Motion.Graph":
							if (_dummyData[_point] > 15)
								Static_EventDispatcher.dispatchEvent(new Event(Graph.MOTION_CHANGE_MOD));
							else
								Static_EventDispatcher.dispatchEvent(new Event(Graph.MOTION_CHANGE_LOW));
							break;
						case "People.Graph":
							Static_EventDispatcher.dispatchEvent(new Event(Graph.PEOPLE_ADD_MINUS));
					}
					
					TweenLite.to(_obj, .7, {y: _testArr[_point], ease: EaseLookup.find(_easing), onComplete:
						function() : void
						{
							TweenLite.to(_obj, .7, {y: _ground, ease: EaseLookup.find(_easing)});
							TweenLite.delayedCall(.5, _moveLine);
						}});
				}
				else
				{
					if (_graphType == "Audio.Graph")
						Static_EventDispatcher.dispatchEvent(new Event(Graph.AUDIO_CHANGE));
				
					TweenLite.to(_obj, .5, {y: _testArr[_point], ease: EaseLookup.find(_easing), onComplete:
						function() : void
						{
							//Static_EventDispatcher.dispatchEvent(new Event(Graph.AUDIO_CHANGE));
							
							TweenLite.to(_obj, .5, {y: _ground, ease: EaseLookup.find(_easing)});
							TweenLite.delayedCall(.5, _moveLine);
						}});
				}
				
				_point++;
		}
		
		/**
		 * Method currently converts the entered value
		 * based off of what the ground is. Not sure if I actually
		 * need this anymore.
		 *  
		 * @param $value
		 * @return 
		 */		
		private function _convertNum($value:Number) : Number
		{
			return _ground - $value;
		}
		
		private function _destroyTween() : void
		{
			TweenLite.killTweensOf(_obj);
			
			if (_graphType != "Audio.Graph")
			{
				TweenLite.to(_obj, 1, {y: _testArr[_point], ease: EaseLookup.find(_secEasing), onComplete:
					function() : void
					{
						//*TweenLite.to(_obj, _tweenTime2, {y: _testArr[_point], ease: EaseLookup.find(_easing)});
						TweenLite.to(_obj, 1, {y: _ground, ease: EaseLookup.find(_easing)});
					}});
			}
			else
			{
				Static_EventDispatcher.dispatchEvent(new Event(Graph.AUDIO_CHANGE));
				
				TweenLite.to(_obj, .5, {y: _testArr[_point], ease: EaseLookup.find(_secEasing), onComplete:
					function() : void
					{
						//Static_EventDispatcher.dispatchEvent(new Event(Graph.AUDIO_CHANGE));
						
						//*TweenLite.to(_obj, _tweenTime2, {y: _testArr[_point], ease: EaseLookup.find(_easing)});
						TweenLite.to(_obj, 1, {y: _ground, ease: EaseLookup.find(_easing)});
						TweenLite.delayedCall(.5, _moveLine);
					}});
			}
			
			_point++;
		}
		
		private function _runRandomMinMax() : void
		{
			if (_randomMinMax() == 2)
				_destroyTween();
		}
		
		private function _randomMinMax() : Number
		{
			return 1 + (2 - 1) * Math.random();
		}
		
		/**
		 * A lot of objects are being added to the scene,
		 * when the cpu-usage gets to a certain point, we need
		 * to get rid of a lot of things.
		 */
		public  function cleanUp() : void
		{
			try
			{
				_canvas.removeEventListener(Event.ENTER_FRAME, _loop);
				TweenLite.killDelayedCallsTo(_moveLine);
				
				this.removeChild(_canvas);
				_graphArr.length = 0;
				this.removeChild(MovieClip(_graphObj));
				
				_canvas = null;
				_p = null;
				_motion = null;
				
				while (numChildren > 0)
					removeChildAt(0);
				
				_createCanvas();
			} catch ($ : ArgumentError)
			{
				// ...
			}
			
			// the GC will perform a full mark/sweep on the second call.
			try {
				new LocalConnection().connect('foo');
				new LocalConnection().connect('foo');
			} catch (e:*) {}
		}
		
		/**
		 * Method that returns _easing
		 * 
		 * @return String
		 */		
		public function get easing() : String
		{
			return _easing;
		}
		
		/**
		 * Method that returns _tweenTime1
		 * 
		 * @return int
		 */
		public function get tweenTime1() : int
		{
			return _tweenTime1;
		}
		
		/**
		 * Return the falue of _tweenTime2
		 * 
		 * @return int
		 */		
		public function get tweenTime2() : int
		{
			return _tweenTime2;
		}
		
		/**
		 * Return the value of _canvasWidth
		 *  
		 * @return Number
		 */		
		public function get canvasWidth() : Number
		{
			return _canvasWidth;
		}
		
		/**
		 * Method that sets the value of _canvasWidth
		 * 
		 * @param $value
		 */		
		public function set canvasWidth($value:Number) : void
		{
			_canvasWidth = $value;
		}
		
		/**
		 * @Override toString()
		 * return: String
		 */
		override public function toString() : String
		{
			return "Easing: " + this.easing + 
				"\nTweenTime1: " + this.tweenTime1 +
				"\nTweenTime2: " + this.tweenTime2;
		}
	}
}